package web

import (
	"context"

	"gitee.com/mkwchecking/sr-core/app"
	"gitee.com/mkwchecking/sr-core/config"
	"gitee.com/mkwchecking/sr-core/config/source"
	"gitee.com/mkwchecking/sr-core/domain"
	"gitee.com/mkwchecking/sr-core/log"
	"gitee.com/mkwchecking/sr-core/log/adapter/klog"
	"gitee.com/mkwchecking/sr-core/registry"
	"gitee.com/mkwchecking/sr-core/relay"
	"github.com/go-kratos/kratos/v2"
	"github.com/go-kratos/kratos/v2/transport"
)

func NewApp(ctx context.Context, opts ...source.Option) (app.IApp, error) {
	src, err := source.New(opts...)
	if err != nil {
		return nil, err
	}

	if err := src.Scan(config.Get()); err != nil {
		return nil, err
	}

	if config.Get().AutoInit {
		constructor := relay.GetConstructor()
		ctx, err = constructor.Install(context.Background(), config.Get())
		if err != nil {
			return nil, err
		}
	} else if logCfg := config.Get().Log; logCfg != nil {
		_, err = log.Install(*logCfg)
		if err != nil {
			return nil, err
		}
	}

	origin := app.New(
		app.WithConfig(config.Get().Application),
		app.WithContext(ctx),
		app.WithSource(src),
	).(*app.YApp)

	logger, _ := log.FromCtx(origin.Context())
	if logger == nil {
		logger = log.GetDefault()
	}

	return &WebApp{
		origin: origin,
		logger: logger,
	}, nil
}

var _ app.IApp = (*WebApp)(nil)

type WebApp struct {
	origin *app.YApp
	kapp   *kratos.App
	logger log.Logger

	servers []transport.Server
	domains []transport.Server
}

// AddServer implements app.IApp.
func (w *WebApp) AddServer(srv transport.Server) error {
	w.servers = append(w.servers, srv)
	return nil
}

// Install implements app.IApp.
func (w *WebApp) Install(ctx context.Context, force bool) error {
	return w.origin.Install(ctx, force)
}

func (w *WebApp) Source() source.ISource {
	return w.origin.Source()
}

// Context implements app.IApp.
func (w *WebApp) Context() context.Context {
	return w.origin.Context()
}

// Meta implements app.IApp.
func (w *WebApp) Meta() map[string]string {
	return w.origin.Meta()
}

// Start implements app.IApp.
func (w *WebApp) Start(ctx context.Context) error {
	app, err := w.initKapp()
	if err != nil {
		return err
	}

	return app.Run()
}

func (w *WebApp) initKapp() (*kratos.App, error) {
	kappCfgs := []kratos.Option{
		kratos.Context(w.origin.Context()),
		kratos.Endpoint(config.Get().Application.GetEndpoint()),
		kratos.Name(config.Get().Application.Hostname),
		kratos.Name(config.Get().Application.Name),
		kratos.Version(config.Get().Application.Version),
		kratos.Logger(klog.New(w.logger)),
		kratos.Metadata(w.origin.Meta()),
		kratos.ID(config.Get().Application.Id),
		kratos.Server(append(w.domains, w.servers...)...),
		kratos.BeforeStart(func(ctx context.Context) error {
			for _, d := range w.origin.Domains() {
				if err := d.BeforeStart(w.origin.Source()); err != nil {
					return err
				}
			}
			return nil
		}),
	}

	if regist, ok := registry.FromCtx(w.origin.Context()); ok {
		kappCfgs = append(kappCfgs,
			kratos.Registrar(regist.GetRegistry()),
		)
	}

	kapp := kratos.New(kappCfgs...)

	return kapp, nil
}

// Stop implements app.IApp.
func (w *WebApp) Stop(ctx context.Context) error {
	if err := w.origin.Stop(ctx); err != nil {
		return err
	}

	return w.kapp.Stop()
}

// WithModule implements app.IApp.
func (w *WebApp) AddDomain(module domain.IDomain) app.IApp {
	w.origin.AddDomain(module)
	w.domains = append(w.domains, module)
	return w
}
